package com.kexilo.system.auth.controller;

import com.kexilo.core.common.annotation.Debounce;
import com.kexilo.core.common.annotation.RateLimiter;
import com.kexilo.core.common.core.web.BaseController;
import com.kexilo.core.common.core.web.domain.R;
import com.kexilo.core.common.utils.SecurityUtils;
import com.kexilo.core.common.utils.StringUtils;
import com.kexilo.core.security.domain.LoginUser;
import com.kexilo.core.security.domain.RefreshToken;
import com.kexilo.core.security.dto.UserDTO;
import com.kexilo.core.security.service.IUserService;
import com.kexilo.system.auth.dto.RefreshTokenRequest;
import com.kexilo.system.auth.service.CaptchaService;
import com.kexilo.system.auth.service.IRefreshTokenService;
import com.kexilo.system.auth.service.SysLoginService;
import com.kexilo.core.security.service.TokenService;

import com.kexilo.system.auth.vo.TokenResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import jakarta.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 登录验证
 * 
 * @author Kexilo
 */
@RestController
@RequestMapping("/auth")
@Tag(name = "认证管理", description = "用户认证相关接口")
public class SysLoginController extends BaseController {
    
    private static final Logger log = LoggerFactory.getLogger(SysLoginController.class);
    
    @Autowired
    private SysLoginService loginService;

    @Autowired
    private TokenService tokenService;
    
        @Autowired
    private CaptchaService captchaService;

    @Autowired(required = false)
    private IUserService userService;

    @Autowired
    private IRefreshTokenService refreshTokenService;

    /**
     * 登录方法
     * 
     * @param loginBody 登录信息
     * @return 结果
     */
    @RateLimiter(key = "login", limitType = RateLimiter.LimitType.IP, count = 5, time = 60, message = "登录过于频繁，请稍后再试")
    @Debounce(strategy = Debounce.Strategy.IP_METHOD, interval = 2, message = "请勿频繁点击登录")
    @PostMapping("/login")
    @Operation(summary = "用户登录", description = "用户名密码登录认证")
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "登录成功"),
        @ApiResponse(responseCode = "400", description = "参数错误"),
        @ApiResponse(responseCode = "401", description = "认证失败")
    })
    public R<TokenResponse> login(@RequestBody LoginBody loginBody, HttpServletRequest request) {
        try {
            // 生成访问令牌
            String accessToken = loginService.login(loginBody.getUsername(), loginBody.getPassword(), 
                                            loginBody.getCode(), loginBody.getUuid());
            
            // 获取用户信息
            LoginUser loginUser = tokenService.getLoginUser(request);
            if (loginUser == null) {
                // 如果无法从当前请求获取，则通过token获取
                loginUser = (LoginUser) SecurityUtils.getLoginUser();
            }
            
            if (loginUser != null) {
                // 获取设备信息
                String deviceId = getDeviceId(request);
                String userAgent = request.getHeader("User-Agent");
                String ipAddress = getClientIP(request);
                
                // 创建刷新令牌
                RefreshToken refreshToken = refreshTokenService.createRefreshToken(
                    loginUser.getUserId(), accessToken, deviceId, userAgent, ipAddress);
                
                // 构建响应
                TokenResponse tokenResponse = new TokenResponse(
                    accessToken, 
                    refreshToken.getRefreshToken(), 
                    7200L, // 2小时过期
                    loginUser.getUserId(),
                    loginUser.getUsername()
                );
                
                return success(tokenResponse);
            } else {
                // 兼容旧版本，返回简单token
                TokenResponse tokenResponse = new TokenResponse(accessToken, null, 7200L);
                return success(tokenResponse);
            }
        } catch (Exception e) {
            log.error("登录失败", e);
            return R.error("登录失败：" + e.getMessage());
        }
    }

    /**
     * 获取用户信息
     * 
     * @return 用户信息
     */
    @GetMapping("/getInfo")
    @Operation(summary = "获取用户信息", description = "根据token获取当前登录用户信息")
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "获取成功"),
        @ApiResponse(responseCode = "401", description = "未认证")
    })
    public R<Map<String, Object>> getInfo() {
        LoginUser user = (LoginUser) SecurityUtils.getLoginUser();
        
        Map<String, Object> result = new HashMap<>();
        result.put("user", getUserInfo(user));
        result.put("roles", getRoles(user));
        result.put("permissions", getPermissions(user));
        
        return success(result);
    }

    /**
     * 获取路由信息
     * 
     * @return 路由信息
     */
    @GetMapping("/getRouters")
    @Operation(summary = "获取路由信息", description = "获取当前用户的菜单路由信息")
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "获取成功"),
        @ApiResponse(responseCode = "401", description = "未认证")
    })
    public R<Object> getRouters() {
        LoginUser user = (LoginUser) SecurityUtils.getLoginUser();
        
        // TODO: 根据用户权限构建菜单树
        // List<SysMenu> menus = menuService.selectMenuTreeByUserId(user.getUserId());
        // return success(menuService.buildMenus(menus));
        
        // 临时返回空的路由信息
        return success((Object) "[]");
    }

    /**
     * 退出登录
     */
    @PostMapping("/logout")
    @Operation(summary = "退出登录", description = "用户退出登录")
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "退出成功")
    })
    public R<Void> logout(HttpServletRequest request) {
        LoginUser loginUser = tokenService.getLoginUser(request);
        if (StringUtils.isNotNull(loginUser)) {
            // 退出登录
            loginService.logout(loginUser);
        }
        return success("退出成功");
    }

    /**
     * 获取验证码
     */
    @GetMapping("/captcha")
    @Operation(summary = "获取验证码", description = "获取图形验证码")
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "获取成功")
    })
    public R<Map<String, Object>> getCaptcha() {
        try {
            // 使用验证码服务生成验证码
            R<CaptchaService.CaptchaInfo> captchaResult = captchaService.generateCaptcha();
            
            if (captchaResult.getCode() == 200 && captchaResult.getData() != null) {
                CaptchaService.CaptchaInfo captchaInfo = captchaResult.getData();
                
                Map<String, Object> result = new HashMap<>();
                result.put("uuid", captchaInfo.getCaptchaKey());
                result.put("img", captchaInfo.getCaptchaImage());
                result.put("captchaEnabled", true);
                
                return success(result);
            } else {
                return R.fail("验证码生成失败");
            }
            
        } catch (Exception e) {
            log.error("获取验证码异常", e);
            return R.fail("验证码获取失败");
        }
    }

    /**
     * 获取用户基本信息
     */
    private Map<String, Object> getUserInfo(LoginUser user) {
        Map<String, Object> userInfo = new HashMap<>();
        
        try {
            // 从数据库获取完整用户信息
            if (userService != null) {
                UserDTO sysUser = userService.selectUserDTOById(user.getUserId());
                
                if (sysUser != null) {
                    userInfo.put("userId", sysUser.getUserId());
                    userInfo.put("userName", sysUser.getUserName());
                    userInfo.put("nickName", StringUtils.isNotEmpty(sysUser.getNickName()) ? sysUser.getNickName() : sysUser.getUserName());
                    userInfo.put("email", StringUtils.nvl(sysUser.getEmail(), ""));
                    userInfo.put("phonenumber", StringUtils.nvl(sysUser.getPhonenumber(), ""));
                    userInfo.put("sex", StringUtils.nvl(sysUser.getSex(), ""));
                    userInfo.put("avatar", StringUtils.nvl(sysUser.getAvatar(), ""));
                    userInfo.put("deptId", sysUser.getDeptId());
                    userInfo.put("status", sysUser.getStatus());
                    userInfo.put("createTime", sysUser.getCreateTime());
                    userInfo.put("loginDate", user.getLoginTime());
                    userInfo.put("loginIp", user.getIpaddr());
                } else {
                    // 如果数据库查询失败，使用LoginUser中的基本信息
                    log.warn("用户信息查询为空: userId={}", user.getUserId());
                    fillBasicUserInfo(userInfo, user);
                }
            } else {
                // 用户服务不可用，使用LoginUser中的基本信息
                log.warn("用户服务不可用，使用基本信息: userId={}", user.getUserId());
                fillBasicUserInfo(userInfo, user);
            }
            
        } catch (Exception e) {
            log.error("获取用户信息异常: userId={}, error={}", user.getUserId(), e.getMessage(), e);
            // 异常情况下使用LoginUser中的基本信息
            fillBasicUserInfo(userInfo, user);
        }
        
        return userInfo;
    }
    
    /**
     * 填充基本用户信息
     */
    private void fillBasicUserInfo(Map<String, Object> userInfo, LoginUser user) {
        userInfo.put("userId", user.getUserId());
        userInfo.put("userName", user.getUsername());
        userInfo.put("nickName", user.getUsername());
        userInfo.put("email", "");
        userInfo.put("phonenumber", "");
        userInfo.put("sex", "");
        userInfo.put("avatar", "");
        userInfo.put("deptId", user.getDeptId());
        userInfo.put("status", "0"); // 默认启用状态
        userInfo.put("createTime", null);
        userInfo.put("loginDate", user.getLoginTime());
        userInfo.put("loginIp", user.getIpaddr());
    }


    /**
     * 获取用户角色信息
     */
    private Set<String> getRoles(LoginUser user) {
        // TODO: 实际项目中应该返回角色编码集合
        // return user.getRoles().stream().map(SysRole::getRoleKey).collect(Collectors.toSet());
        
        // 临时实现
        if ("admin".equals(user.getUsername())) {
            return Set.of("admin");
        }
        return Set.of("common");
    }

    /**
     * 获取用户权限信息
     */
    private Set<String> getPermissions(LoginUser user) {
        return user.getPermissions();
    }

    /**
     * 登录请求体
     */
    public static class LoginBody {
        /**
         * 用户名
         */
        private String username;

        /**
         * 用户密码
         */
        private String password;

        /**
         * 验证码
         */
        private String code;

        /**
         * 唯一标识
         */
        private String uuid = "";

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public String getCode() {
            return code;
        }

        public void setCode(String code) {
            this.code = code;
        }

        public String getUuid() {
            return uuid;
        }

        public void setUuid(String uuid) {
            this.uuid = uuid;
        }
    }

    /**
     * 生成验证码
     */
    @RateLimiter(key = "captcha", limitType = RateLimiter.LimitType.IP, count = 10, time = 60, message = "验证码获取过于频繁，请稍后再试")
    @Debounce(strategy = Debounce.Strategy.IP_METHOD, interval = 1, message = "请勿频繁获取验证码")
    @GetMapping("/captcha")
    @Operation(summary = "生成验证码", description = "生成图形验证码")
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "生成成功")
    })
    public R<CaptchaService.CaptchaInfo> captcha() {
        return captchaService.generateCaptcha();
    }

    /**
     * 刷新访问令牌
     */
    @PostMapping("/refresh")
    @Operation(summary = "刷新令牌", description = "使用刷新令牌获取新的访问令牌")
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "刷新成功"),
        @ApiResponse(responseCode = "401", description = "刷新令牌无效")
    })
    public R<TokenResponse> refresh(@RequestBody RefreshTokenRequest request, HttpServletRequest httpRequest) {
        try {
            String refreshTokenValue = request.getRefreshToken();
            
            // 验证刷新令牌
            if (!refreshTokenService.validateRefreshToken(refreshTokenValue)) {
                return R.error("刷新令牌无效或已过期");
            }
            
            // 获取刷新令牌信息
            RefreshToken refreshToken = refreshTokenService.findByRefreshToken(refreshTokenValue);
            if (refreshToken == null) {
                return R.error("刷新令牌不存在");
            }
            
            // 生成新的访问令牌
            LoginUser loginUser = new LoginUser();
            loginUser.setUserId(refreshToken.getUserId());
            // TODO: 从用户服务获取完整用户信息
            
            String newAccessToken = tokenService.createToken(loginUser);
            
            // 更新刷新令牌关联的访问令牌
            refreshTokenService.refreshAccessToken(refreshTokenValue, newAccessToken);
            
            // 构建响应
            TokenResponse tokenResponse = new TokenResponse(
                newAccessToken,
                refreshTokenValue, // 复用原刷新令牌
                7200L, // 2小时过期
                refreshToken.getUserId(),
                null // username需要从用户服务获取
            );
            
            return success(tokenResponse);
            
        } catch (Exception e) {
            log.error("刷新令牌失败", e);
            return R.error("刷新令牌失败：" + e.getMessage());
        }
    }

    /**
     * 撤销刷新令牌
     */
    @PostMapping("/revoke")
    @Operation(summary = "撤销令牌", description = "撤销指定的刷新令牌")
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "撤销成功")
    })
    public R<Void> revoke(@RequestBody RefreshTokenRequest request) {
        try {
            boolean success = refreshTokenService.revokeRefreshToken(request.getRefreshToken());
            if (success) {
                return success("令牌撤销成功");
            } else {
                return R.error("令牌撤销失败");
            }
        } catch (Exception e) {
            log.error("撤销令牌失败", e);
            return R.error("撤销令牌失败：" + e.getMessage());
        }
    }

    /**
     * 获取设备ID
     */
    private String getDeviceId(HttpServletRequest request) {
        String deviceId = request.getHeader("X-Device-Id");
        if (StringUtils.isEmpty(deviceId)) {
            // 如果没有设备ID，使用User-Agent的hash作为设备标识
            String userAgent = request.getHeader("User-Agent");
            if (StringUtils.isNotEmpty(userAgent)) {
                deviceId = "DEV_" + Math.abs(userAgent.hashCode());
            } else {
                deviceId = "UNKNOWN_DEVICE";
            }
        }
        return deviceId;
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIP(HttpServletRequest request) {
        String ipAddress = request.getHeader("X-Forwarded-For");
        if (StringUtils.isEmpty(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isEmpty(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isEmpty(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
        }
        return ipAddress;
    }
}
